// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

float4x4 matWorldViewProjection;
float4 fvCameraPos;
float4 fvLightPos;

float3 fvInvWavelengths;
float fCameraHeightSqr;
float fInnerRadius;
float fOuterRadius;
float fOuterRadiusSqr;
float fKr4PI;
float fKm4PI;
float fKrESun;
float fKmESun;
float fScale;
float fScaleOverScaleDepth;


#define nSamples 3
#define fSamples ((float)nSamples)
#define fInvScaleDepth (1.0f / 0.25)
#define fScaleDepth 0.25


// Returns the near intersection point of a line and a sphere
float getNearIntersection(float3 v3Pos, float3 v3Ray, float fDistance2, float fRadius2)
{
   float B = 2.0 * dot(v3Pos, v3Ray);
   float C = fDistance2 - fRadius2;
   float fDet = max(0.0, B*B - 4.0 * C);
   return 0.5 * (-B - sqrt(fDet));
}

// The scale equation calculated by Vernier's Graphical Analysis
float scale(float fCos)
{
   float x = 1.0 - fCos;
   return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}

struct VS_OUTPUT
{
   float4 Position : POSITION0;      // Transformed vertex position
   float4 RayleighColor : TEXCOORD0;         // The Rayleigh color
   float4 MieColor : TEXCOORD1;         // The Mie color
   float3 Texcoord : TEXCOORD2;
};

struct VS_INPUT
{
	float4 Position : POSITION0;
};


VS_OUTPUT vs_main(VS_INPUT Input)
{
   VS_OUTPUT Output;

   float3 fvPos = Input.Position.xyz;
   float3 fvRay = fvPos - fvCameraPos.xyz;

   fvPos = normalize(fvPos);
   float fFar = length(fvRay);
   fvRay /= fFar;


   // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)
   float fNear = getNearIntersection(fvCameraPos, fvRay, fCameraHeightSqr, fOuterRadiusSqr);

   // Calculate the ray's starting position, then calculate its scattering offset
   float3 fvStart = fvCameraPos + fvRay * fNear;
   fFar -= fNear;


   // Calculate the ray's start and end positions in the atmosphere, then calculate its scattering offset
   float fStartAngle = dot(fvRay, fvStart) / fOuterRadius;
   float fStartDepth = exp(-fInvScaleDepth);
   float fStartOffset = fStartDepth*scale(fStartAngle);


   float fSampleLength = fFar / fSamples;
   float fScaledLength = fSampleLength * fScale;
   float3 fvSampleRay = fvRay * fSampleLength;
   float3 fvSamplePoint = fvStart + fvSampleRay * 0.5;


   // Now loop through the sample rays
   float3 fvFrontColor = float3(0.0, 0.0, 0.0);
   for(int i=0; i<nSamples; i++)
   {
      float fHeight = length(fvSamplePoint);
      float fSampleDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
      float fLightAngle = dot(fvLightPos.xyz, fvSamplePoint) / fHeight;
      float fCameraAngle = dot(fvRay, fvSamplePoint) / fHeight;
      float fScatter = (fStartOffset + fSampleDepth *(scale(fLightAngle) - scale(fCameraAngle)));
      float3 fvAttenuate = exp(-fScatter * (fvInvWavelengths * fKr4PI + fKm4PI));
      fvFrontColor += fvAttenuate * (fSampleDepth * fScaledLength);
      fvSamplePoint += fvSampleRay;
   }


   Output.Position = mul(matWorldViewProjection, Input.Position);
   Output.RayleighColor = float4(fvFrontColor * (fvInvWavelengths * fKrESun),1.0);
   Output.MieColor = float4(fvFrontColor * fKmESun,1.0);
   Output.Texcoord = fvCameraPos.xyz - fvPos;

   return Output;
}

